
#include "asm/disassembler.h"
#include "os/os.h"

namespace l8
{


enum OperandType
{
    UNSET_OP_ORDER = 0,
    // Operand size decides between 16, 32 and 64 bit operands.
    REG_OPER_OP_ORDER = 1,  // Register destination, operand source.
    OPER_REG_OP_ORDER = 2,  // Operand destination, register source.
    // Fixed 8-bit operands.
    BYTE_SIZE_OPERAND_FLAG = 4,
    BYTE_REG_OPER_OP_ORDER = REG_OPER_OP_ORDER | BYTE_SIZE_OPERAND_FLAG,
    BYTE_OPER_REG_OP_ORDER = OPER_REG_OP_ORDER | BYTE_SIZE_OPERAND_FLAG
};

//------------------------------------------------------------------
// Tables
//------------------------------------------------------------------
struct ByteMnemonic
{
    int b;  // -1 terminates, otherwise must be in range (0..255)
    OperandType op_order_;
    const char* mnem;
};


static ByteMnemonic two_operands_instr[] =
{
    { 0x00, BYTE_OPER_REG_OP_ORDER, "add" },
    { 0x01, OPER_REG_OP_ORDER,      "add" },
    { 0x02, BYTE_REG_OPER_OP_ORDER, "add" },
    { 0x03, REG_OPER_OP_ORDER,      "add" },
    { 0x08, BYTE_OPER_REG_OP_ORDER, "or" },
    { 0x09, OPER_REG_OP_ORDER,      "or" },
    { 0x0A, BYTE_REG_OPER_OP_ORDER, "or" },
    { 0x0B, REG_OPER_OP_ORDER,      "or" },
    { 0x10, BYTE_OPER_REG_OP_ORDER, "adc" },
    { 0x11, OPER_REG_OP_ORDER,      "adc" },
    { 0x12, BYTE_REG_OPER_OP_ORDER, "adc" },
    { 0x13, REG_OPER_OP_ORDER,      "adc" },
    { 0x18, BYTE_OPER_REG_OP_ORDER, "sbb" },
    { 0x19, OPER_REG_OP_ORDER,      "sbb" },
    { 0x1A, BYTE_REG_OPER_OP_ORDER, "sbb" },
    { 0x1B, REG_OPER_OP_ORDER,      "sbb" },
    { 0x20, BYTE_OPER_REG_OP_ORDER, "and" },
    { 0x21, OPER_REG_OP_ORDER,      "and" },
    { 0x22, BYTE_REG_OPER_OP_ORDER, "and" },
    { 0x23, REG_OPER_OP_ORDER,      "and" },
    { 0x28, BYTE_OPER_REG_OP_ORDER, "sub" },
    { 0x29, OPER_REG_OP_ORDER,      "sub" },
    { 0x2A, BYTE_REG_OPER_OP_ORDER, "sub" },
    { 0x2B, REG_OPER_OP_ORDER,      "sub" },
    { 0x30, BYTE_OPER_REG_OP_ORDER, "xor" },
    { 0x31, OPER_REG_OP_ORDER,      "xor" },
    { 0x32, BYTE_REG_OPER_OP_ORDER, "xor" },
    { 0x33, REG_OPER_OP_ORDER,      "xor" },
    { 0x38, BYTE_OPER_REG_OP_ORDER, "cmp" },
    { 0x39, OPER_REG_OP_ORDER,      "cmp" },
    { 0x3A, BYTE_REG_OPER_OP_ORDER, "cmp" },
    { 0x3B, REG_OPER_OP_ORDER,      "cmp" },
    { 0x63, REG_OPER_OP_ORDER,      "movsxlq" },
    { 0x84, BYTE_REG_OPER_OP_ORDER, "test" },
    { 0x85, REG_OPER_OP_ORDER,      "test" },
    { 0x86, BYTE_REG_OPER_OP_ORDER, "xchg" },
    { 0x87, REG_OPER_OP_ORDER,      "xchg" },
    { 0x88, BYTE_OPER_REG_OP_ORDER, "mov" },
    { 0x89, OPER_REG_OP_ORDER,      "mov" },
    { 0x8A, BYTE_REG_OPER_OP_ORDER, "mov" },
    { 0x8B, REG_OPER_OP_ORDER,      "mov" },
    { 0x8D, REG_OPER_OP_ORDER,      "lea" },
    { -1, UNSET_OP_ORDER, "" }
};


static ByteMnemonic zero_operands_instr[] =
{
    { 0xC3, UNSET_OP_ORDER, "ret" },
    { 0xC9, UNSET_OP_ORDER, "leave" },
    { 0xF4, UNSET_OP_ORDER, "hlt" },
    { 0xCC, UNSET_OP_ORDER, "int3" },
    { 0x60, UNSET_OP_ORDER, "pushad" },
    { 0x61, UNSET_OP_ORDER, "popad" },
    { 0x9C, UNSET_OP_ORDER, "pushfd" },
    { 0x9D, UNSET_OP_ORDER, "popfd" },
    { 0x9E, UNSET_OP_ORDER, "sahf" },
    { 0x99, UNSET_OP_ORDER, "cdq" },
    { 0x9B, UNSET_OP_ORDER, "fwait" },
    { -1, UNSET_OP_ORDER, "" }
};


static ByteMnemonic call_jump_instr[] =
{
    { 0xE8, UNSET_OP_ORDER, "call" },
    { 0xE9, UNSET_OP_ORDER, "jmp" },
    { -1, UNSET_OP_ORDER, "" }
};


static ByteMnemonic short_immediate_instr[] =
{
    { 0x05, UNSET_OP_ORDER, "add" },
    { 0x0D, UNSET_OP_ORDER, "or" },
    { 0x15, UNSET_OP_ORDER, "adc" },
    { 0x1D, UNSET_OP_ORDER, "sbb" },
    { 0x25, UNSET_OP_ORDER, "and" },
    { 0x2D, UNSET_OP_ORDER, "sub" },
    { 0x35, UNSET_OP_ORDER, "xor" },
    { 0x3D, UNSET_OP_ORDER, "cmp" },
    { -1, UNSET_OP_ORDER, "" }
};


static const char* conditional_code_suffix[] =
{
    "o", "no", "c", "nc", "z", "nz", "na", "a",
    "s", "ns", "pe", "po", "l", "ge", "le", "g"
};


enum InstructionType
{
    NO_INSTR,
    ZERO_OPERANDS_INSTR,
    TWO_OPERANDS_INSTR,
    JUMP_CONDITIONAL_SHORT_INSTR,
    REGISTER_INSTR,
    PUSHPOP_INSTR,  // Has implicit 64-bit operand size.
    MOVE_REG_INSTR,
    CALL_JUMP_INSTR,
    SHORT_IMMEDIATE_INSTR
};


struct InstructionDesc
{
    const char* mnem;
    InstructionType type;
    OperandType op_order_;
    bool byte_size_operation;  // Fixed 8-bit operation.
};


class InstructionTable
{
public:
    InstructionTable();

    const InstructionDesc& Get(byte x) const
    {
        return instructions_[x];
    }

private:
    InstructionDesc instructions_[256];

    void Clear();
    void Init();
    void CopyTable(ByteMnemonic bm[], InstructionType type);
    void SetTableRange(InstructionType type, byte start, byte end, bool byte_size,
                       const char* mnem);
    void AddJumpConditionalShort();
};


InstructionTable::InstructionTable()
{
    Clear();
    Init();
}


void InstructionTable::Clear()
{
    for (int i = 0; i < 256; i++)
    {
        instructions_[i].mnem = "(bad)";
        instructions_[i].type = NO_INSTR;
        instructions_[i].op_order_ = UNSET_OP_ORDER;
        instructions_[i].byte_size_operation = false;
    }
}


void InstructionTable::Init()
{
    CopyTable(two_operands_instr, TWO_OPERANDS_INSTR);
    CopyTable(zero_operands_instr, ZERO_OPERANDS_INSTR);
    CopyTable(call_jump_instr, CALL_JUMP_INSTR);
    CopyTable(short_immediate_instr, SHORT_IMMEDIATE_INSTR);
    AddJumpConditionalShort();
    SetTableRange(PUSHPOP_INSTR, 0x50, 0x57, false, "push");
    SetTableRange(PUSHPOP_INSTR, 0x58, 0x5F, false, "pop");
    SetTableRange(MOVE_REG_INSTR, 0xB8, 0xBF, false, "mov");
}


void InstructionTable::CopyTable(ByteMnemonic bm[], InstructionType type)
{
    for (int i = 0; bm[i].b >= 0; i++)
    {
        InstructionDesc* id = &instructions_[bm[i].b];
        id->mnem = bm[i].mnem;
        OperandType op_order = bm[i].op_order_;
        id->op_order_ =
                static_cast<OperandType>(op_order & ~BYTE_SIZE_OPERAND_FLAG);
        assert(id->type == NO_INSTR);  // Information not already entered
        id->type = type;
        id->byte_size_operation = ((op_order & BYTE_SIZE_OPERAND_FLAG) != 0);
    }
}


void InstructionTable::SetTableRange(InstructionType type,
                                     byte start,
                                     byte end,
                                     bool byte_size,
                                     const char* mnem)
{
    for (byte b = start; b <= end; b++)
    {
        InstructionDesc* id = &instructions_[b];
        assert(id->type == NO_INSTR);  // Information already entered
        id->mnem = mnem;
        id->type = type;
        id->byte_size_operation = byte_size;
    }
}


void InstructionTable::AddJumpConditionalShort()
{
    for (byte b = 0x70; b <= 0x7F; b++)
    {
        InstructionDesc* id = &instructions_[b];
        assert(id->type == NO_INSTR);  // Information already entered
        id->mnem = NULL;  // Computed depending on condition code.
        id->type = JUMP_CONDITIONAL_SHORT_INSTR;
    }
}


static InstructionTable instruction_table;

static InstructionDesc cmov_instructions[16] =
{
    {"cmovo", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
    {"cmovno", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
    {"cmovc", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
    {"cmovnc", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
    {"cmovz", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
    {"cmovnz", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
    {"cmovna", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
    {"cmova", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
    {"cmovs", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
    {"cmovns", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
    {"cmovpe", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
    {"cmovpo", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
    {"cmovl", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
    {"cmovge", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
    {"cmovle", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false},
    {"cmovg", TWO_OPERANDS_INSTR, REG_OPER_OP_ORDER, false}
};

//------------------------------------------------------------------------------
// DisassemblerX64 implementation.

enum UnimplementedOpcodeAction
{
    CONTINUE_ON_UNIMPLEMENTED_OPCODE,
    ABORT_ON_UNIMPLEMENTED_OPCODE
};

// A new DisassemblerX64 object is created to disassemble each instruction.
// The object can only disassemble a single instruction.
class DisassemblerX64
{
public:
    DisassemblerX64(const NameConverter& converter,
                    UnimplementedOpcodeAction unimplemented_action =
                    ABORT_ON_UNIMPLEMENTED_OPCODE)
    : converter_(converter),
    tmp_buffer_pos_(0),
    abort_on_unimplemented_(
            unimplemented_action == ABORT_ON_UNIMPLEMENTED_OPCODE),
    rex_(0),
    operand_size_(0),
    group_1_prefix_(0),
    byte_size_operand_(false) {
        tmp_buffer_[0] = '\0';
    }

    virtual ~DisassemblerX64()
    {
    }

    // Writes one disassembled instruction into 'buffer' (0-terminated).
    // Returns the length of the disassembled machine instruction in bytes.
    int InstructionDecode(Vector<char> buffer, byte* instruction);

private:
    enum OperandSize
    {
        BYTE_SIZE = 0,
        WORD_SIZE = 1,
        DOUBLEWORD_SIZE = 2,
        QUADWORD_SIZE = 3,
    };

    const NameConverter& converter_;
    EmbeddedVector<char, 128> tmp_buffer_;
    unsigned int tmp_buffer_pos_;
    bool abort_on_unimplemented_;

    // Prefixes parsed
    byte rex_;
    byte operand_size_;  // 0x66 or (if no group 3 prefix is present) 0x0.
    byte group_1_prefix_;  // 0xF2, 0xF3, or (if no group 1 prefix is present) 0.
    // Byte size operand override.
    bool byte_size_operand_;

    void setRex(byte rex)
    {
        ASSERT_EQ(0x40, rex & 0xF0);
        rex_ = rex;
    }

    bool rex() { return rex_ != 0; }

    bool rex_b() { return (rex_ & 0x01) != 0; }

    // Actual number of base register given the low bits and the rex.b state.
    int base_reg(int low_bits) { return low_bits | ((rex_ & 0x01) << 3); }

    bool rex_x() { return (rex_ & 0x02) != 0; }

    bool rex_r() { return (rex_ & 0x04) != 0; }

    bool rex_w() { return (rex_ & 0x08) != 0; }

    OperandSize operand_size() {
        if (byte_size_operand_) return BYTE_SIZE;
        if (rex_w()) return QUADWORD_SIZE;
        if (operand_size_ != 0) return WORD_SIZE;
        return DOUBLEWORD_SIZE;
    }

    char operand_size_code() {
        return "bwlq"[operand_size()];
    }

    const char* NameOfCPURegister(int reg) const {
        return converter_.NameOfCPURegister(reg);
    }

    const char* NameOfByteCPURegister(int reg) const {
        return converter_.NameOfByteCPURegister(reg);
    }

    const char* NameOfXMMRegister(int reg) const {
        return converter_.NameOfXMMRegister(reg);
    }

    const char* NameOfAddress(byte* addr) const {
        return converter_.NameOfAddress(addr);
    }

    // Disassembler helper functions.
    void get_modrm(byte data,
                   int* mod,
                   int* regop,
                   int* rm) {
        *mod = (data >> 6) & 3;
        *regop = ((data & 0x38) >> 3) | (rex_r() ? 8 : 0);
        *rm = (data & 7) | (rex_b() ? 8 : 0);
    }

    void get_sib(byte data,
                 int* scale,
                 int* index,
                 int* base) {
        *scale = (data >> 6) & 3;
        *index = ((data >> 3) & 7) | (rex_x() ? 8 : 0);
        *base = (data & 7) | (rex_b() ? 8 : 0);
    }

    typedef const char* (DisassemblerX64::*RegisterNameMapping)(int reg) const;

    int PrintRightOperandHelper(byte* modrmp,
                                RegisterNameMapping register_name);
    int PrintRightOperand(byte* modrmp);
    int PrintRightByteOperand(byte* modrmp);
    int PrintOperands(const char* mnem,
                      OperandType op_order,
                      byte* data);
    int PrintImmediate(byte* data, OperandSize size);
    int PrintImmediateOp(byte* data);
    const char* TwoByteMnemonic(byte opcode);
    int TwoByteOpcodeInstruction(byte* data);
    int F7Instruction(byte* data);
    int ShiftInstruction(byte* data);
    int JumpShort(byte* data);
    int JumpConditional(byte* data);
    int JumpConditionalShort(byte* data);
    int SetCC(byte* data);
    int FPUInstruction(byte* data);
    void AppendToBuffer(const char* format, ...);

    void UnimplementedInstruction() {
        if (abort_on_unimplemented_) {
            CHECK(false);
        } else {
            AppendToBuffer("'Unimplemented Instruction'");
        }
    }
};


void DisassemblerX64::AppendToBuffer(const char* format, ...) {
    Vector<char> buf = tmp_buffer_ + tmp_buffer_pos_;
    va_list args;
    va_start(args, format);
    int result = OS::VSNPrintF(buf, format, args);
    va_end(args);
    tmp_buffer_pos_ += result;
}


int DisassemblerX64::PrintRightOperandHelper(
        byte* modrmp,
        RegisterNameMapping register_name) {
    int mod, regop, rm;
    get_modrm(*modrmp, &mod, &regop, &rm);
    switch (mod) {
    case 0:
        if ((rm & 7) == 5) {
            int32_t disp = *reinterpret_cast<int32_t*>(modrmp + 1);
            AppendToBuffer("[0x%x]", disp);
            return 5;
        } else if ((rm & 7) == 4) {
            // Codes for SIB byte.
            byte sib = *(modrmp + 1);
            int scale, index, base;
            get_sib(sib, &scale, &index, &base);
            if (index == 4 && (base & 7) == 4 && scale == 0 /*times_1*/) {
                // index == rsp means no index. Only use sib byte with no index for
                // rsp and r12 base.
                AppendToBuffer("[%s]", (this->*register_name)(base));
                return 2;
            } else if (base == 5) {
                // base == rbp means no base register (when mod == 0).
                int32_t disp = *reinterpret_cast<int32_t*>(modrmp + 2);
                AppendToBuffer("[%s*%d+0x%x]",
                               (this->*register_name)(index),
                               1 << scale, disp);
                return 6;
            } else if (index != 4 && base != 5) {
                // [base+index*scale]
                AppendToBuffer("[%s+%s*%d]",
                               (this->*register_name)(base),
                               (this->*register_name)(index),
                               1 << scale);
                return 2;
            } else {
                UnimplementedInstruction();
                return 1;
            }
        } else {
            AppendToBuffer("[%s]", (this->*register_name)(rm));
            return 1;
        }
        break;
    case 1:  // fall through
    case 2:
        if ((rm & 7) == 4) {
            byte sib = *(modrmp + 1);
            int scale, index, base;
            get_sib(sib, &scale, &index, &base);
            int disp = (mod == 2) ? *reinterpret_cast<int32_t*>(modrmp + 2)
                       : *reinterpret_cast<char*>(modrmp + 2);
            if (index == 4 && (base & 7) == 4 && scale == 0 /*times_1*/) {
                if (-disp > 0) {
                    AppendToBuffer("[%s-0x%x]", (this->*register_name)(base), -disp);
                } else {
                    AppendToBuffer("[%s+0x%x]", (this->*register_name)(base), disp);
                }
            } else {
                if (-disp > 0) {
                    AppendToBuffer("[%s+%s*%d-0x%x]",
                                   (this->*register_name)(base),
                                   (this->*register_name)(index),
                                   1 << scale,
                                   -disp);
                } else {
                    AppendToBuffer("[%s+%s*%d+0x%x]",
                                   (this->*register_name)(base),
                                   (this->*register_name)(index),
                                   1 << scale,
                                   disp);
                }
            }
            return mod == 2 ? 6 : 3;
        } else {
            // No sib.
            int disp = (mod == 2) ? *reinterpret_cast<int32_t*>(modrmp + 1)
                       : *reinterpret_cast<char*>(modrmp + 1);
            if (-disp > 0) {
                AppendToBuffer("[%s-0x%x]", (this->*register_name)(rm), -disp);
            } else {
                AppendToBuffer("[%s+0x%x]", (this->*register_name)(rm), disp);
            }
            return (mod == 2) ? 5 : 2;
        }
        break;
    case 3:
        AppendToBuffer("%s", (this->*register_name)(rm));
        return 1;
    default:
        UnimplementedInstruction();
        return 1;
    }
    UNREACHABLE();
}


int DisassemblerX64::PrintImmediate(byte* data, OperandSize size) {
    int64_t value;
    int count;
    switch (size) {
    case BYTE_SIZE:
        value = *data;
        count = 1;
        break;
    case WORD_SIZE:
        value = *reinterpret_cast<int16_t*>(data);
        count = 2;
        break;
    case DOUBLEWORD_SIZE:
        value = *reinterpret_cast<uint32_t*>(data);
        count = 4;
        break;
    case QUADWORD_SIZE:
        value = *reinterpret_cast<int32_t*>(data);
        count = 4;
        break;
    default:
        UNREACHABLE();
        value = 0;  // Initialize variables on all paths to satisfy the compiler.
        count = 0;
    }
    AppendToBuffer("%" PRINT_PTR_PREFIX "x", value);
    return count;
}


int DisassemblerX64::PrintRightOperand(byte* modrmp) {
    return PrintRightOperandHelper(modrmp,
                                   &DisassemblerX64::NameOfCPURegister);
}


int DisassemblerX64::PrintRightByteOperand(byte* modrmp) {
    return PrintRightOperandHelper(modrmp,
                                   &DisassemblerX64::NameOfByteCPURegister);
}


// Returns number of bytes used including the current *data.
// Writes instruction's mnemonic, left and right operands to 'tmp_buffer_'.
int DisassemblerX64::PrintOperands(const char* mnem,
                                   OperandType op_order,
                                   byte* data) {
    byte modrm = *data;
    int mod, regop, rm;
    get_modrm(modrm, &mod, &regop, &rm);
    int advance = 0;
    const char* register_name =
            byte_size_operand_ ? NameOfByteCPURegister(regop)
            : NameOfCPURegister(regop);
    switch (op_order) {
    case REG_OPER_OP_ORDER: {
            AppendToBuffer("%s%c %s,",
                           mnem,
                           operand_size_code(),
                           register_name);
            advance = byte_size_operand_ ? PrintRightByteOperand(data)
                      : PrintRightOperand(data);
            break;
        }
    case OPER_REG_OP_ORDER: {
            AppendToBuffer("%s%c ", mnem, operand_size_code());
            advance = byte_size_operand_ ? PrintRightByteOperand(data)
                      : PrintRightOperand(data);
            AppendToBuffer(",%s", register_name);
            break;
        }
    default:
        UNREACHABLE();
        break;
    }
    return advance;
}


// Returns number of bytes used by machine instruction, including *data byte.
// Writes immediate instructions to 'tmp_buffer_'.
int DisassemblerX64::PrintImmediateOp(byte* data) {
    bool byte_size_immediate = (*data & 0x02) != 0;
    byte modrm = *(data + 1);
    int mod, regop, rm;
    get_modrm(modrm, &mod, &regop, &rm);
    const char* mnem = "Imm???";
    switch (regop) {
    case 0:
        mnem = "add";
        break;
    case 1:
        mnem = "or";
        break;
    case 2:
        mnem = "adc";
        break;
    case 4:
        mnem = "and";
        break;
    case 5:
        mnem = "sub";
        break;
    case 6:
        mnem = "xor";
        break;
    case 7:
        mnem = "cmp";
        break;
    default:
        UnimplementedInstruction();
    }
    AppendToBuffer("%s%c ", mnem, operand_size_code());
    int count = PrintRightOperand(data + 1);
    AppendToBuffer(",0x");
    OperandSize immediate_size = byte_size_immediate ? BYTE_SIZE : operand_size();
    count += PrintImmediate(data + 1 + count, immediate_size);
    return 1 + count;
}


// Returns number of bytes used, including *data.
int DisassemblerX64::F7Instruction(byte* data) {
    assert(*data == 0xF7);
    byte modrm = *(data + 1);
    int mod, regop, rm;
    get_modrm(modrm, &mod, &regop, &rm);
    if (mod == 3 && regop != 0) {
        const char* mnem = NULL;
        switch (regop) {
        case 2:
            mnem = "not";
            break;
        case 3:
            mnem = "neg";
            break;
        case 4:
            mnem = "mul";
            break;
        case 7:
            mnem = "idiv";
            break;
        default:
            UnimplementedInstruction();
        }
        AppendToBuffer("%s%c %s",
                       mnem,
                       operand_size_code(),
                       NameOfCPURegister(rm));
        return 2;
    } else if (mod == 3 && regop == 0) {
        int32_t imm = *reinterpret_cast<int32_t*>(data + 2);
        AppendToBuffer("test%c %s,0x%x",
                       operand_size_code(),
                       NameOfCPURegister(rm),
                       imm);
        return 6;
    } else if (regop == 0) {
        AppendToBuffer("test%c ", operand_size_code());
        int count = PrintRightOperand(data + 1);
        int32_t imm = *reinterpret_cast<int32_t*>(data + 1 + count);
        AppendToBuffer(",0x%x", imm);
        return 1 + count + 4 /*int32_t*/;
    } else {
        UnimplementedInstruction();
        return 2;
    }
}


int DisassemblerX64::ShiftInstruction(byte* data) {
    byte op = *data & (~1);
    if (op != 0xD0 && op != 0xD2 && op != 0xC0) {
        UnimplementedInstruction();
        return 1;
    }
    byte modrm = *(data + 1);
    int mod, regop, rm;
    get_modrm(modrm, &mod, &regop, &rm);
    regop &= 0x7;  // The REX.R bit does not affect the operation.
    int imm8 = -1;
    int num_bytes = 2;
    if (mod != 3) {
        UnimplementedInstruction();
        return num_bytes;
    }
    const char* mnem = NULL;
    switch (regop) {
    case 0:
        mnem = "rol";
        break;
    case 1:
        mnem = "ror";
        break;
    case 2:
        mnem = "rcl";
        break;
    case 3:
        mnem = "rcr";
        break;
    case 4:
        mnem = "shl";
        break;
    case 5:
        mnem = "shr";
        break;
    case 7:
        mnem = "sar";
        break;
    default:
        UnimplementedInstruction();
        return num_bytes;
    }
    assert(mnem != NULL);
    if (op == 0xD0) {
        imm8 = 1;
    } else if (op == 0xC0) {
        imm8 = *(data + 2);
        num_bytes = 3;
    }
    AppendToBuffer("%s%c %s,",
                   mnem,
                   operand_size_code(),
                   byte_size_operand_ ? NameOfByteCPURegister(rm)
                   : NameOfCPURegister(rm));
    if (op == 0xD2) {
        AppendToBuffer("cl");
    } else {
        AppendToBuffer("%d", imm8);
    }
    return num_bytes;
}


// Returns number of bytes used, including *data.
int DisassemblerX64::JumpShort(byte* data) {
    assert(*data == 0xEB);
    byte b = *(data + 1);
    byte* dest = data + static_cast<int8_t>(b) + 2;
    AppendToBuffer("jmp %s", NameOfAddress(dest));
    return 2;
}


// Returns number of bytes used, including *data.
int DisassemblerX64::JumpConditional(byte* data) {
    assert(*data == 0x0F);
    byte cond = *(data + 1) & 0x0F;
    byte* dest = data + *reinterpret_cast<int32_t*>(data + 2) + 6;
    const char* mnem = conditional_code_suffix[cond];
    AppendToBuffer("j%s %s", mnem, NameOfAddress(dest));
    return 6;  // includes 0x0F
}


// Returns number of bytes used, including *data.
int DisassemblerX64::JumpConditionalShort(byte* data) {
    byte cond = *data & 0x0F;
    byte b = *(data + 1);
    byte* dest = data + static_cast<int8_t>(b) + 2;
    const char* mnem = conditional_code_suffix[cond];
    AppendToBuffer("j%s %s", mnem, NameOfAddress(dest));
    return 2;
}


// Returns number of bytes used, including *data.
int DisassemblerX64::SetCC(byte* data) {
    assert(*data == 0x0F);
    byte cond = *(data + 1) & 0x0F;
    const char* mnem = conditional_code_suffix[cond];
    AppendToBuffer("set%s%c ", mnem, operand_size_code());
    PrintRightByteOperand(data + 2);
    return 3;  // includes 0x0F
}


// Returns number of bytes used, including *data.
int DisassemblerX64::FPUInstruction(byte* data) {
    byte b1 = *data;
    byte b2 = *(data + 1);
    if (b1 == 0xD9) {
        const char* mnem = NULL;
        switch (b2) {
        case 0xE0:
            mnem = "fchs";
            break;
        case 0xE1:
            mnem = "fabs";
            break;
        case 0xE4:
            mnem = "ftst";
            break;
        case 0xF5:
            mnem = "fprem1";
            break;
        case 0xF7:
            mnem = "fincstp";
            break;
        case 0xE8:
            mnem = "fld1";
            break;
        case 0xEE:
            mnem = "fldz";
            break;
        case 0xF8:
            mnem = "fprem";
            break;
        }
        if (mnem != NULL) {
            AppendToBuffer("%s", mnem);
            return 2;
        } else if ((b2 & 0xF8) == 0xC8) {
            AppendToBuffer("fxch st%d", b2 & 0x7);
            return 2;
        } else {
            int mod, regop, rm;
            get_modrm(*(data + 1), &mod, &regop, &rm);
            const char* mnem = "?";
            switch (regop) {
            case 0:
                mnem = "fld_s";
                break;
            case 3:
                mnem = "fstp_s";
                break;
            default:
                UnimplementedInstruction();
            }
            AppendToBuffer("%s ", mnem);
            int count = PrintRightOperand(data + 1);
            return count + 1;
        }
    } else if (b1 == 0xDD) {
        if ((b2 & 0xF8) == 0xC0) {
            AppendToBuffer("ffree st%d", b2 & 0x7);
            return 2;
        } else {
            int mod, regop, rm;
            get_modrm(*(data + 1), &mod, &regop, &rm);
            const char* mnem = "?";
            switch (regop) {
            case 0:
                mnem = "fld_d";
                break;
            case 3:
                mnem = "fstp_d";
                break;
            default:
                UnimplementedInstruction();
            }
            AppendToBuffer("%s ", mnem);
            int count = PrintRightOperand(data + 1);
            return count + 1;
        }
    } else if (b1 == 0xDB) {
        int mod, regop, rm;
        get_modrm(*(data + 1), &mod, &regop, &rm);
        const char* mnem = "?";
        switch (regop) {
        case 0:
            mnem = "fild_s";
            break;
        case 2:
            mnem = "fist_s";
            break;
        case 3:
            mnem = "fistp_s";
            break;
        default:
            UnimplementedInstruction();
        }
        AppendToBuffer("%s ", mnem);
        int count = PrintRightOperand(data + 1);
        return count + 1;
    } else if (b1 == 0xDF) {
        if (b2 == 0xE0) {
            AppendToBuffer("fnstsw_ax");
            return 2;
        }
        int mod, regop, rm;
        get_modrm(*(data + 1), &mod, &regop, &rm);
        const char* mnem = "?";
        switch (regop) {
        case 5:
            mnem = "fild_d";
            break;
        case 7:
            mnem = "fistp_d";
            break;
        default:
            UnimplementedInstruction();
        }
        AppendToBuffer("%s ", mnem);
        int count = PrintRightOperand(data + 1);
        return count + 1;
    } else if (b1 == 0xDC || b1 == 0xDE) {
        bool is_pop = (b1 == 0xDE);
        if (is_pop && b2 == 0xD9) {
            AppendToBuffer("fcompp");
            return 2;
        }
        const char* mnem = "FP0xDC";
        switch (b2 & 0xF8) {
        case 0xC0:
            mnem = "fadd";
            break;
        case 0xE8:
            mnem = "fsub";
            break;
        case 0xC8:
            mnem = "fmul";
            break;
        case 0xF8:
            mnem = "fdiv";
            break;
        default:
            UnimplementedInstruction();
        }
        AppendToBuffer("%s%s st%d", mnem, is_pop ? "p" : "", b2 & 0x7);
        return 2;
    } else if (b1 == 0xDA && b2 == 0xE9) {
        const char* mnem = "fucompp";
        AppendToBuffer("%s", mnem);
        return 2;
    }
    AppendToBuffer("Unknown FP instruction");
    return 2;
}


// Handle all two-byte opcodes, which start with 0x0F.
// These instructions may be affected by an 0x66, 0xF2, or 0xF3 prefix.
// We do not use any three-byte opcodes, which start with 0x0F38 or 0x0F3A.
int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) {
    byte opcode = *(data + 1);
    byte* current = data + 2;
    // At return, "current" points to the start of the next instruction.
    const char* mnemonic = TwoByteMnemonic(opcode);
    if (opcode == 0x1F) {
        // NOP
        int mod, regop, rm;
        get_modrm(*current, &mod, &regop, &rm);
        current++;
        if (regop == 4) {  // SIB byte present.
            current++;
        }
        if (mod == 1) {  // Byte displacement.
            current += 1;
        } else if (mod == 2) {  // 32-bit displacement.
            current += 4;
        }  // else no immediate displacement.
        AppendToBuffer("nop");

    } else  if (opcode == 0xA2 || opcode == 0x31) {
        // RDTSC or CPUID
        AppendToBuffer("%s", mnemonic);

    } else if ((opcode & 0xF0) == 0x40) {
        // CMOVcc: conditional move.
        int condition = opcode & 0x0F;
        const InstructionDesc& idesc = cmov_instructions[condition];
        byte_size_operand_ = idesc.byte_size_operation;
        current += PrintOperands(idesc.mnem, idesc.op_order_, current);

    } else if ((opcode & 0xF0) == 0x80) {
        // Jcc: Conditional jump (branch).
        current = data + JumpConditional(data);

    } else if (opcode == 0xBE || opcode == 0xBF || opcode == 0xB6 ||
               opcode == 0xB7 || opcode == 0xAF) {
        // Size-extending moves, IMUL.
        current += PrintOperands(mnemonic, REG_OPER_OP_ORDER, current);

    } else if ((opcode & 0xF0) == 0x90) {
        // SETcc: Set byte on condition. Needs pointer to beginning of instruction.
        current = data + SetCC(data);

    } else if (opcode == 0xAB || opcode == 0xA5 || opcode == 0xAD) {
        // SHLD, SHRD (double-precision shift), BTS (bit set).
        AppendToBuffer("%s ", mnemonic);
        int mod, regop, rm;
        get_modrm(*current, &mod, &regop, &rm);
        current += PrintRightOperand(current);
        if (opcode == 0xAB) {
            AppendToBuffer(",%s", NameOfCPURegister(regop));
        } else {
            AppendToBuffer(",%s,cl", NameOfCPURegister(regop));
        }
    } else if (group_1_prefix_ == 0xF2) {
        // Beginning of instructions with prefix 0xF2.

        if (opcode == 0x11 || opcode == 0x10) {
            // MOVSD: Move scalar double-precision fp to/from/between XMM registers.
            AppendToBuffer("movsd ");
            int mod, regop, rm;
            get_modrm(*current, &mod, &regop, &rm);
            if (opcode == 0x11) {
                current += PrintRightOperand(current);
                AppendToBuffer(",%s", NameOfXMMRegister(regop));
            } else {
                AppendToBuffer("%s,", NameOfXMMRegister(regop));
                current += PrintRightOperand(current);
            }
        } else if (opcode == 0x2A) {
            // CVTSI2SD: integer to XMM double conversion.
            int mod, regop, rm;
            get_modrm(*current, &mod, &regop, &rm);
            AppendToBuffer("%s %s,", mnemonic, NameOfXMMRegister(regop));
            data += PrintRightOperand(data);
        } else if ((opcode & 0xF8) == 0x58) {
            // XMM arithmetic. Mnemonic was retrieved at the start of this function.
            int mod, regop, rm;
            get_modrm(*current, &mod, &regop, &rm);
            AppendToBuffer("%s %s,%s", mnemonic, NameOfXMMRegister(regop),
                           NameOfXMMRegister(rm));
        } else {
            UnimplementedInstruction();
        }
    } else if (opcode == 0x2C && group_1_prefix_ == 0xF3) {
        // Instruction with prefix 0xF3.

        // CVTTSS2SI: Convert scalar single-precision FP to dword integer.
        // Assert that mod is not 3, so source is memory, not an XMM register.
        ASSERT((*current & 0xC0) != 0xC0);
        current += PrintOperands("cvttss2si", REG_OPER_OP_ORDER, current);
    } else {
        UnimplementedInstruction();
    }
    return current - data;
}


// Mnemonics for two-byte opcode instructions starting with 0x0F.
// The argument is the second byte of the two-byte opcode.
// Returns NULL if the instruction is not handled here.
const char* DisassemblerX64::TwoByteMnemonic(byte opcode) {
    switch (opcode) {
    case 0x1F:
        return "nop";
    case 0x2A:  // F2 prefix.
        return "cvtsi2sd";
    case 0x31:
        return "rdtsc";
    case 0x58:  // F2 prefix.
        return "addsd";
    case 0x59:  // F2 prefix.
        return "mulsd";
    case 0x5C:  // F2 prefix.
        return "subsd";
    case 0x5E:  // F2 prefix.
        return "divsd";
    case 0xA2:
        return "cpuid";
    case 0xA5:
        return "shld";
    case 0xAB:
        return "bts";
    case 0xAD:
        return "shrd";
    case 0xAF:
        return "imul";
    case 0xB6:
        return "movzxb";
    case 0xB7:
        return "movzxw";
    case 0xBE:
        return "movsxb";
    case 0xBF:
        return "movsxw";
    default:
        return NULL;
    }
}


// Disassembles the instruction at instr, and writes it into out_buffer.
int DisassemblerX64::InstructionDecode(Vector<char> out_buffer,
                                       byte* instr) {
    tmp_buffer_pos_ = 0;  // starting to write as position 0
    byte* data = instr;
    bool processed = true;  // Will be set to false if the current instruction
    // is not in 'instructions' table.
    byte current;

    // Scan for prefixes.
    while (true) {
        current = *data;
        if (current == 0x66) {  // Group 3 prefix.
            operand_size_ = current;
        } else if ((current & 0xF0) == 0x40) {  // REX prefix.
            setRex(current);
            if (rex_w()) AppendToBuffer("REX.W ");
        } else if ((current & 0xFE) == 0xF2) {  // Group 1 prefix.
            group_1_prefix_ = current;
        } else {  // Not a prefix - an opcode.
            break;
        }
        data++;
    }

    const InstructionDesc& idesc = instruction_table.Get(current);
    byte_size_operand_ = idesc.byte_size_operation;
    switch (idesc.type) {
    case ZERO_OPERANDS_INSTR:
        AppendToBuffer(idesc.mnem);
        data++;
        break;

    case TWO_OPERANDS_INSTR:
        data++;
        data += PrintOperands(idesc.mnem, idesc.op_order_, data);
        break;

    case JUMP_CONDITIONAL_SHORT_INSTR:
        data += JumpConditionalShort(data);
        break;

    case REGISTER_INSTR:
        AppendToBuffer("%s%c %s",
                       idesc.mnem,
                       operand_size_code(),
                       NameOfCPURegister(base_reg(current & 0x07)));
        data++;
        break;
    case PUSHPOP_INSTR:
        AppendToBuffer("%s %s",
                       idesc.mnem,
                       NameOfCPURegister(base_reg(current & 0x07)));
        data++;
        break;
    case MOVE_REG_INSTR: {
            byte* addr = NULL;
            switch (operand_size()) {
            case WORD_SIZE:
                addr = reinterpret_cast<byte*>(*reinterpret_cast<int16_t*>(data + 1));
                data += 3;
                break;
            case DOUBLEWORD_SIZE:
                addr = reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(data + 1));
                data += 5;
                break;
            case QUADWORD_SIZE:
                addr = reinterpret_cast<byte*>(*reinterpret_cast<int64_t*>(data + 1));
                data += 9;
                break;
            default:
                UNREACHABLE();
            }
            AppendToBuffer("mov%c %s,%s",
                           operand_size_code(),
                           NameOfCPURegister(base_reg(current & 0x07)),
                           NameOfAddress(addr));
            break;
        }

    case CALL_JUMP_INSTR: {
            byte* addr = data + *reinterpret_cast<int32_t*>(data + 1) + 5;
            AppendToBuffer("%s %s", idesc.mnem, NameOfAddress(addr));
            data += 5;
            break;
        }

    case SHORT_IMMEDIATE_INSTR: {
            byte* addr =
                    reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(data + 1));
            AppendToBuffer("%s rax, %s", idesc.mnem, NameOfAddress(addr));
            data += 5;
            break;
        }

    case NO_INSTR:
        processed = false;
        break;

    default:
        UNIMPLEMENTED();  // This type is not implemented.
    }

    // The first byte didn't match any of the simple opcodes, so we
    // need to do special processing on it.
    if (!processed) {
        switch (*data) {
        case 0xC2:
            AppendToBuffer("ret 0x%x", *reinterpret_cast<uint16_t*>(data + 1));
            data += 3;
            break;

        case 0x69:  // fall through
        case 0x6B: {
                int mod, regop, rm;
                get_modrm(*(data + 1), &mod, &regop, &rm);
                int32_t imm = *data == 0x6B ? *(data + 2)
                              : *reinterpret_cast<int32_t*>(data + 2);
                AppendToBuffer("imul %s,%s,0x%x", NameOfCPURegister(regop),
                               NameOfCPURegister(rm), imm);
                data += 2 + (*data == 0x6B ? 1 : 4);
                break;
            }

        case 0xF6: {
                int mod, regop, rm;
                get_modrm(*(data + 1), &mod, &regop, &rm);
                if (mod == 3 && regop == 0) {
                    AppendToBuffer("testb %s,%d", NameOfCPURegister(rm), *(data + 2));
                } else {
                    UnimplementedInstruction();
                }
                data += 3;
                break;
            }

        case 0x81:  // fall through
        case 0x83:  // 0x81 with sign extension bit set
            data += PrintImmediateOp(data);
            break;

        case 0x0F:
            data += TwoByteOpcodeInstruction(data);
            break;

        case 0x8F: {
                data++;
                int mod, regop, rm;
                get_modrm(*data, &mod, &regop, &rm);
                if (regop == 0) {
                    AppendToBuffer("pop ");
                    data += PrintRightOperand(data);
                }
            }
            break;

        case 0xFF: {
                data++;
                int mod, regop, rm;
                get_modrm(*data, &mod, &regop, &rm);
                const char* mnem = NULL;
                switch (regop) {
                case 0:
                    mnem = "inc";
                    break;
                case 1:
                    mnem = "dec";
                    break;
                case 2:
                    mnem = "call";
                    break;
                case 4:
                    mnem = "jmp";
                    break;
                case 6:
                    mnem = "push";
                    break;
                default:
                    mnem = "???";
                }
                AppendToBuffer(((regop <= 1) ? "%s%c " : "%s "),
                               mnem,
                               operand_size_code());
                data += PrintRightOperand(data);
            }
            break;

        case 0xC7:  // imm32, fall through
        case 0xC6:  // imm8
            {
                bool is_byte = *data == 0xC6;
                data++;

                AppendToBuffer("mov%c ", is_byte ? 'b' : operand_size_code());
                data += PrintRightOperand(data);
                int32_t imm = is_byte ? *data : *reinterpret_cast<int32_t*>(data);
                AppendToBuffer(",0x%x", imm);
                data += is_byte ? 1 : 4;
            }
            break;

        case 0x80: {
                data++;
                AppendToBuffer("cmpb ");
                data += PrintRightOperand(data);
                int32_t imm = *data;
                AppendToBuffer(",0x%x", imm);
                data++;
            }
            break;

        case 0x88:  // 8bit, fall through
        case 0x89:  // 32bit
            {
                bool is_byte = *data == 0x88;
                int mod, regop, rm;
                data++;
                get_modrm(*data, &mod, &regop, &rm);
                AppendToBuffer("mov%c ", is_byte ? 'b' : operand_size_code());
                data += PrintRightOperand(data);
                AppendToBuffer(",%s", NameOfCPURegister(regop));
            }
            break;

        case 0x90:
        case 0x91:
        case 0x92:
        case 0x93:
        case 0x94:
        case 0x95:
        case 0x96:
        case 0x97: {
                int reg = (current & 0x7) | (rex_b() ? 8 : 0);
                if (reg == 0) {
                    AppendToBuffer("nop");  // Common name for xchg rax,rax.
                } else {
                    AppendToBuffer("xchg%c rax, %s",
                                   operand_size_code(),
                                   NameOfCPURegister(reg));
                }
            }


        case 0xFE: {
                data++;
                int mod, regop, rm;
                get_modrm(*data, &mod, &regop, &rm);
                if (mod == 3 && regop == 1) {
                    AppendToBuffer("decb %s", NameOfCPURegister(rm));
                } else {
                    UnimplementedInstruction();
                }
                data++;
            }
            break;

        case 0x68:
            AppendToBuffer("push 0x%x", *reinterpret_cast<int32_t*>(data + 1));
            data += 5;
            break;

        case 0x6A:
            AppendToBuffer("push 0x%x", *reinterpret_cast<int8_t*>(data + 1));
            data += 2;
            break;

        case 0xA1:  // Fall through.
        case 0xA3:
            switch (operand_size()) {
            case DOUBLEWORD_SIZE: {
                    const char* memory_location = NameOfAddress(
                            reinterpret_cast<byte*>(
                                    *reinterpret_cast<int32_t*>(data + 1)));
                    if (*data == 0xA1) {  // Opcode 0xA1
                        AppendToBuffer("movzxlq rax,(%s)", memory_location);
                    } else {  // Opcode 0xA3
                        AppendToBuffer("movzxlq (%s),rax", memory_location);
                    }
                    data += 5;
                    break;
                }
            case QUADWORD_SIZE: {
                    // New x64 instruction mov rax,(imm_64).
                    const char* memory_location = NameOfAddress(
                            *reinterpret_cast<byte**>(data + 1));
                    if (*data == 0xA1) {  // Opcode 0xA1
                        AppendToBuffer("movq rax,(%s)", memory_location);
                    } else {  // Opcode 0xA3
                        AppendToBuffer("movq (%s),rax", memory_location);
                    }
                    data += 9;
                    break;
                }
            default:
                UnimplementedInstruction();
                data += 2;
            }
            break;

        case 0xA8:
            AppendToBuffer("test al,0x%x", *reinterpret_cast<uint8_t*>(data + 1));
            data += 2;
            break;

        case 0xA9: {
                int64_t value = 0;
                switch (operand_size()) {
                case WORD_SIZE:
                    value = *reinterpret_cast<uint16_t*>(data + 1);
                    data += 3;
                    break;
                case DOUBLEWORD_SIZE:
                    value = *reinterpret_cast<uint32_t*>(data + 1);
                    data += 5;
                    break;
                case QUADWORD_SIZE:
                    value = *reinterpret_cast<int32_t*>(data + 1);
                    data += 5;
                    break;
                default:
                    UNREACHABLE();
                }
                AppendToBuffer("test%c rax,0x%" PRINT_PTR_PREFIX "x",
                               operand_size_code(),
                               value);
                break;
            }
        case 0xD1:  // fall through
        case 0xD3:  // fall through
        case 0xC1:
            data += ShiftInstruction(data);
            break;
        case 0xD0:  // fall through
        case 0xD2:  // fall through
        case 0xC0:
            byte_size_operand_ = true;
            data += ShiftInstruction(data);
            break;

        case 0xD9:  // fall through
        case 0xDA:  // fall through
        case 0xDB:  // fall through
        case 0xDC:  // fall through
        case 0xDD:  // fall through
        case 0xDE:  // fall through
        case 0xDF:
            data += FPUInstruction(data);
            break;

        case 0xEB:
            data += JumpShort(data);
            break;

        case 0xF7:
            data += F7Instruction(data);
            break;

        default:
            UnimplementedInstruction();
            data += 1;
        }
    }  // !processed

    if (tmp_buffer_pos_ < sizeof tmp_buffer_) {
        tmp_buffer_[tmp_buffer_pos_] = '\0';
    }

    int instr_len = data - instr;
    ASSERT(instr_len > 0);  // Ensure progress.

    int outp = 0;
    // Instruction bytes.
    for (byte* bp = instr; bp < data; bp++) {
        outp += OS::SNPrintF(out_buffer + outp, "%02x", *bp);
    }
    for (int i = 6 - instr_len; i >= 0; i--) {
        outp += OS::SNPrintF(out_buffer + outp, "  ");
    }

    outp += OS::SNPrintF(out_buffer + outp, " %s",
                                       tmp_buffer_.start());
    return instr_len;
}

//------------------------------------------------------------------------------


static const char* cpu_regs[16] = {
    "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
    "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"
};


static const char* byte_cpu_regs[16] = {
    "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil",
    "r8l", "r9l", "r10l", "r11l", "r12l", "r13l", "r14l", "r15l"
};


static const char* xmm_regs[16] = {
    "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",
    "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15"
};


const char* NameConverter::NameOfAddress(byte* addr) const {
    static EmbeddedVector<char, 32> tmp_buffer;
    OS::SNPrintF(tmp_buffer, "%p", addr);
    return tmp_buffer.start();
}


const char* NameConverter::NameOfConstant(byte* addr) const {
    return NameOfAddress(addr);
}


const char* NameConverter::NameOfCPURegister(int reg) const {
    if (0 <= reg && reg < 16)
        return cpu_regs[reg];
    return "noreg";
}


const char* NameConverter::NameOfByteCPURegister(int reg) const {
    if (0 <= reg && reg < 16)
        return byte_cpu_regs[reg];
    return "noreg";
}


const char* NameConverter::NameOfXMMRegister(int reg) const {
    if (0 <= reg && reg < 16)
        return xmm_regs[reg];
    return "noxmmreg";
}


const char* NameConverter::NameInCode(byte* addr) const {
    // X64 does not embed debug strings at the moment.
    UNREACHABLE();
    return "";
}

//------------------------------------------------------------------------------

Disassembler::Disassembler(const NameConverter& converter)
        : converter_(converter) { }

Disassembler::~Disassembler() { }


int Disassembler::InstructionDecode(Vector<char> buffer,
                                    byte* instruction) {
    DisassemblerX64 d(converter_, CONTINUE_ON_UNIMPLEMENTED_OPCODE);
    return d.InstructionDecode(buffer, instruction);
}


// The X64 assembler does not use constant pools.
int Disassembler::ConstantPoolSizeAt(byte* instruction) {
    return -1;
}


void Disassembler::Disassemble(FILE* f, byte* begin, byte* end) {
    NameConverter converter;
    Disassembler d(converter);
    for (byte* pc = begin; pc < end;) {
        EmbeddedVector<char, 128> buffer;
        buffer[0] = '\0';
        byte* prev_pc = pc;
        pc += d.InstructionDecode(buffer, pc);
        fprintf(f, "%p", prev_pc);
        fprintf(f, "    ");

        for (byte* bp = prev_pc; bp < pc; bp++) {
            fprintf(f, "%02x", *bp);
        }
        for (int i = 6 - (pc - prev_pc); i >= 0; i--) {
            fprintf(f, "  ");
        }
        fprintf(f, "  %s\n", buffer.start());
    }
}


} // namespace l8
